#coding=utf8

from ala_classes import TokenType as T, Token, AlaUnknownTokenError


class Scanner(object):

    file = None
    text = None
    tokens = None
    caret_position = None
    empty_lines_mapping = None

    #INTERFACE

    def __init__(self, f):
        """
        Абстрактный лексический анализатор. """
        self.file = f
        self.text = u""
        self.tokens = []
        self.caret_position = 0
        self.empty_lines_mapping = {}
        self.__read_from_file()

    def get_next_token(self, current_line):
        """
        Считывает следующий токен и возвращает объект Token. """
        if self.caret_position in range(0, len(self.text) + 1):
            token = self.find_token()
            token.line = current_line
            if token.type == T.ERROR:
                raise AlaUnknownTokenError(u"Неизвестный токен", token.line)
            elif token.type != T.EOI:
                if token.type != T.NONE:
                    self.caret_position += len(token.value)
                    if token.type == T.WHITESPACE:
                        return self.get_next_token(current_line)
                    else:
                        return token
                self.caret_position += 1
                return Token(T.NONE)
            else:
                return token

    #PRIVATE METHODS

    def __read_from_file(self):
        """
        Попутно удаляет пустые строки и строки, состоящие из одних пробелов """
        ch = self.file.read(1)
        while ch != "":
            self.text += ch
            ch = self.file.read(1)
        lines = []
        empty_lines_shift = 0
        for line_number, line in enumerate(self.text.split("\n")):
            if line.replace(" ", "").replace("\t", "") != "":
                lines.append(line)
                self.empty_lines_mapping[line_number - empty_lines_shift + 1] = line_number + 1
            else:
                empty_lines_shift += 1
        self.text = unicode("\n".join(lines))

    @staticmethod
    def is_whitespace(ch):
        """
        Определяет, является ли символ незначимым """
        return ch.isspace() or ch == "\n" or ch == "\t"

    def find_token(self):
        """
        Должен быть переопределен в классе-наследнике"""
        raise NotImplementedError("_find_token() is not implemented")

    def is_empty_line(self, line_number):
        return line_number in self.empty_lines